package io.github.talelin.latticy.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import io.github.talelin.autoconfigure.exception.ForbiddenException;
import io.github.talelin.latticy.common.configuration.FreeMarkerConfig;
import io.github.talelin.latticy.common.configuration.KieConfig;
import io.github.talelin.latticy.common.configuration.MyKieContext;
import io.github.talelin.latticy.common.constant.RuleTemplateConstant;
import io.github.talelin.latticy.common.mybatis.Page;
import io.github.talelin.latticy.dto.rule.chargeItem.ChargeItemDTO;
import io.github.talelin.latticy.dto.rule.chargeItem.ChargePlanItemsDTO;
import io.github.talelin.latticy.dto.rule.chargeItem.UpdateChargeItemDTO;
import io.github.talelin.latticy.model.*;
import io.github.talelin.latticy.mapper.ChargeItemMapper;
import io.github.talelin.latticy.service.*;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import io.github.talelin.latticy.vo.ChargeConditionMarkVO;
import io.github.talelin.latticy.vo.ChargeItemVO;
import org.kie.api.runtime.KieContainer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author generator@wulongbo
 * @since 2022-08-23
 */
@Service
public class ChargeItemServiceImpl extends ServiceImpl<ChargeItemMapper, ChargeItemDO> implements ChargeItemService {

    @Autowired
    private RuleTemplateService ruleTemplateService;

    @Autowired
    private FreeMarkerConfig freeMarkerConfig;

    @Autowired
    private RulesService rulesService;

    @Autowired
    private ChargeAbsoluteService chargeAbsoluteService;

    @Autowired
    private ChargeConditionService chargeConditionService;

    @Autowired
    private ChargePlanItemService chargePlanItemService;

    @Autowired
    private RuleValueService ruleValueService;

    @Autowired
    KieConfig kieConfig;

    @Autowired
    MyKieContext myKieContext;

    @Override
    @Transactional
    public boolean addChargeItem(ChargeItemDTO dto) {
        System.out.println(JSON.toJSONString(dto));
        checkChargeItemExistByName(dto.getName());
        String templateName = ruleTemplateService.getTemplateNameByFeesBizTypeAndLevel(dto.getFeesBizType().getFeesBizType(), dto.getLevel().getLevel());
//        UserDO user = LocalUser.getLocalUser();
//        ChargeItemBO chargeItemBO = new ChargeItemBO(dto, user.getId());
        Map map = JSONObject.parseObject(JSON.toJSONString(dto), Map.class);
        String rule = freeMarkerConfig.getContent(templateName, map);
        // 添加规则
        RulesDO rulesDO = new RulesDO(rule, dto.getName(), dto.getBaseCost());
        rulesService.save(rulesDO);
        // 添加计费项
        ChargeItemDO chargeItemDO = new ChargeItemDO(dto, rulesDO.getId());
        this.save(chargeItemDO);
        // 添加计费 charge_absolute
        List<ChargeAbsoluteDO> caList = dto.getAbsoluteList().parallelStream().map(l -> new ChargeAbsoluteDO(l, chargeItemDO.getId())).collect(Collectors.toList());
        chargeAbsoluteService.saveBatch(caList);
        // 添加计费 charge_condition
        List<ChargeConditionDO> ccList = dto.getConditionList().parallelStream().map(l -> new ChargeConditionDO(l, chargeItemDO.getId())).collect(Collectors.toList());
        chargeConditionService.saveBatch(ccList);
        // 重新加载规则
        KieContainer kieContainer = kieConfig.kieContainer();
        myKieContext.update(kieContainer);
        return true;
    }

    @Transactional
    @Override
    public boolean updateChargeItemById(UpdateChargeItemDTO dto, Integer id) {
        boolean result = this.updateById(new ChargeItemDO(dto, id));
        Assert.isTrue(result, "18809");
        return rulesService.updateRuleNameById(dto.getRuleId(), dto.getName());
    }

    @Transactional
    @Override
    public boolean deleteChargeItemById(Integer id) {
        boolean flag;
        // 1:删除rules规则表
        flag = rulesService.deleteRulesByChargeItemId(id);
        Assert.isTrue(flag, "18811");
        // 2:删除计费项表
        flag = this.removeById(id);
        Assert.isTrue(flag, "18812");
        // 3:删除计费条件表
        chargeAbsoluteService.deleteChargeAbsoluteByChargeItemId(id);
        Assert.isTrue(flag, "18813");
        // 4:删除计费结果表
        chargeAbsoluteService.deleteChargeAbsoluteByChargeItemId(id);
        // 5:删除计费方案关联的计费项
        LambdaQueryWrapper<ChargePlanItemDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ChargePlanItemDO::getChargeItemId, id);
        chargePlanItemService.remove(wrapper);
        return flag;
    }

    @Override
    public ChargeItemVO getChargeItemVOById(Integer id) {
        ChargeItemVO chargeItemVO = this.baseMapper.getChargeItemVOById(id);
        StringBuffer condition = new StringBuffer();
        condition.append(RuleTemplateConstant.IF_CONDITION);
        StringBuffer sb = new StringBuffer();
        List<ChargeConditionMarkVO> conditionList = chargeItemVO.getConditionList();
        if (CollUtil.isNotEmpty(conditionList)) {
            List<ChargeConditionMarkVO> frameType2List =
                    conditionList.parallelStream().filter(c -> c.getFrameType() == RuleTemplateConstant.FRAME_TYPE_2).collect(Collectors.toList());
            if (CollUtil.isNotEmpty(frameType2List)) {
                frameType2List.parallelStream().forEach(
                        x -> {
                            RuleValueDO ruleValueDO = ruleValueService.getById(x.getRuleValue());
                            x.setRuleValue(ruleValueDO.getValue());
                        }
                );
            }
            conditionList.stream().forEach(
                    cd -> {
                        if (StrUtil.isNotEmpty(cd.getLogicalMark())) {
                            sb.append(cd.getLogicalMark());
                        }
                        sb.append(cd.getFltValue()).
                                append(cd.getSymbolMark()).append(cd.getRuleValue())
                                .append(RuleTemplateConstant.STRING_CHAR_FEN);
                    }
            );
        }
        chargeItemVO.setCondition(condition.append(sb).toString());
        StringBuffer absolute = new StringBuffer();
        absolute.append(RuleTemplateConstant.TOTAL_PRICE).append(RuleTemplateConstant.CONNECT_DAO)
                .append(chargeItemVO.getBaseCost());
        StringBuffer stringBuffer = new StringBuffer();
        if (CollUtil.isNotEmpty(chargeItemVO.getAbsoluteList())) {
            chargeItemVO.getAbsoluteList().stream().forEach(
                    ab -> {
                        stringBuffer.append(RuleTemplateConstant.CONNECT_PLUS)
                                .append(RuleTemplateConstant.CONNECT_DAO)
                                .append(ab.getRuleValueId())
                                .append(RuleTemplateConstant.CONNECT_MULTIPLICATION)
                                .append(ab.getFltValue());
                    }
            );
        }
        chargeItemVO.setAbsolute(absolute.append(stringBuffer).toString());
        return chargeItemVO;
    }

    @Override
    public List<ChargeItemDO> getChargeItemList(Integer feesBizType) {
        if (feesBizType == null) {
            return this.baseMapper.selectList(null);
        }
        LambdaQueryWrapper<ChargeItemDO> wrapper = new LambdaQueryWrapper();
        wrapper.eq(ChargeItemDO::getFeesBizType, feesBizType);
        return this.baseMapper.selectList(wrapper);
    }

    @Override
    public IPage<ChargeItemDO> getChargeItemPageByFeesBizType(Integer page, Integer count, String name,
                                                              Integer level, Integer billType, Integer feesBizType) {
        Page<ChargeItemDO> pager = new Page<>(page, count);
        QueryWrapper<ChargeItemDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.lambda().eq(level != null, ChargeItemDO::getLevel, level).eq(billType != null, ChargeItemDO::getBillType, billType)
                .eq(feesBizType != null, ChargeItemDO::getFeesBizType, feesBizType).like(StrUtil.isNotEmpty(name), ChargeItemDO::getName, name);
        return this.baseMapper.selectPage(pager, queryWrapper);
    }

    @Override
    public List<ChargePlanItemsDTO> getItemsByChargePlanById(Integer chargePlanId) {
        List<ChargePlanItemsDTO> chargePlanItems = this.baseMapper.getItemsByChargePlanById(chargePlanId);
        Assert.notNull(chargePlanItems, "18823");
        return chargePlanItems;
    }

    private void checkChargeItemExistByName(String name) {
        QueryWrapper<ChargeItemDO> wrapper = new QueryWrapper<>();
        wrapper.lambda().eq(ChargeItemDO::getName, name);
        int count = this.count(wrapper);
        if (count > 0) {
            throw new ForbiddenException(18802);
        }
    }
}
